package com.tear.language.parser;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;

import com.tear.language.node.BaseNode;
import com.tear.language.node.NodeFactory;
import com.tear.language.utils.Attrs;
import com.tear.language.utils.Regex;

/**

service(
	name:"test",
	do:[
		def(name:'name',value:'"zhangsan"');
		test(desc:'def');
		def(name:'age',value:'11');
		test(desc:'def');
		set(name:'age',value:'age+11');
		test(desc:'set');
		set(name:'name',value:'"lisi"');
		test(desc:'set');
		if(condition:'name=="lisi1111"',do:[
			test(desc:'if');
			set(name:'age',value:'12');
			test(desc:'set')
		]);
		set(name:'lisi',value:'{11,21,31}');
		set(name:'map',value:'#{"name1":"zhangsan","age1":56}');
		while(index:'i',collection:'map',var:'item',do:[
			test(desc:'while');
			set(name:'i',value:'i+1')
		]);
		set(name:'xx',value:'2');
		test(desc:'set');
		result(name:'result',value:'map')
	]
)
	
	
service(
	name:"test",
	do:[
		def(name:'name',value:'\"zhangsan\"');
		def(name:'age',value:12);
		choose(
			do:[
				when(condition:'name == \"zhangsan\"' do:[
					set(name:'age',value:22);
					test(desc:'when');
				]);
				when(condition:'name == \"lisi\"' do:[
					set(name:'age',value:23)
				]);
				other(do:[
					set(name:'age',value:24)
				]);
			]);
		test(desc:'choose');
		print(name:'age')
		
	]
)

service(
	name:'test',
	do:[
		def(name:'map',value:'#{}');
		select(id:'test1',name:'test1');
		set(name:'name',value:'currentUser.username',target:'map');
		set(name:'test1',value:'test1',target:'map');
		result(value:'map')
	]
)



 */

public class JSonParser implements Parser{

	public JSonParser() {
	}
	
	/**
	 * 将变种格式替换成json格式
	 * @param str
	 * @return
	 */
	public static JSONObject transferJson(String str) {
		//去掉多行注释
		str = str.replaceAll(Regex.MULTILINE_COMMENT,"");
		//转换do[成do:[
		str = str.replaceAll(Regex.DO, "do:[");
		//转换)else成);else
		str = str.replaceAll(Regex.ELSE, ");else(");
		str = "{"+str+"}";
		str = str.replaceAll(";\\s*]","]");
		str = str.replace("(", ":{");
		str = str.replace(")","}");
		str = str.replace(";","},{");
		str = str.replace("[", "<-{");
		str = str.replace("]", "}->");
		str = str.replace("<-", "[");
		str = str.replace("->", "]");
		System.out.println(str);
		return JSONObject.fromObject(str);
	}
	
	/**
	 	以如下原始语言格式为
	 	service(
		    name:'test',do[
		        set(name:'a',value:1);
		        set(name:'b',value:0);
		        def(name:'c',value:0);
		        if(condition:'a==0',do[
		            set(name:'b',value:1);
		            test(desc:'b1');
		        ]) 
		        else(condition:'a==2',do [
		                set(name:'b',value:2);
		                test(desc:'b2');
		        ]) 
		        else( do [
		            set(name:'b',value:3);
		            set(name:'c',value:3);
		            def(name:'d',value:3);
		            test(desc:'b3');
		            if(condition:'b==2',do [
		                    set(name:'e',value:1);
		                    test(desc:'e1');
		            ])
		            else(do [
		                    set(name:'e',value:2);
		                    test(desc:'e2');
		            ])
		        ]);
		        test(desc:'d4')
		])
		转成json格式之后如下
		{
		    service: {
		        name: 'test',
		        do: [
		            {set: {name: 'a',value: 1}},
		            {set: {name: 'b',value: 0}},
		            {def: {name: 'c',value: 0}},
		            {if: {condition: 'a==0',
		                do: [
		                    {set: {name: 'b',value: 1}},
		                    {test: {desc: 'b1'}}
		                ]}
		            },
		            {else: { condition: 'a==2',
		                do: [
		                    {set: {name: 'b',value: 2}},
		                    {test: {desc: 'b2'}}
		                ]}
		            },
		            {else: {
		                do: [
		                    {set: {name: 'b',value: 3}},
		                    {set: {name: 'c',value: 3}},
		                    {def: {name: 'd',value: 3}},
		                    {test: {desc: 'b3'}},
		                    {if: { condition: 'b==2',
		                         do: [
		                             {set: {name: 'e',value: 1}},
		                             {test: {desc: 'e1'}}
		                         ]}
		                    },
		                    {else: {
		                           do: [
		                               {set: {name: 'e',value: 2}},
		                               {test: {desc: 'e2'}}
		                           ]}
		                        }
		                    ]
		                }
		            },
		            {test: {desc: 'd4'}}
		        ]
		    }
		}
		
	 */
	public static void main(String[] args) throws Exception {
		
		String str = "service(\n" +
                "    name:'test',do[\n" +
                "        set(name:'a',value:1);\n" +
                "        set(name:'b',value:0);\n" +
                "        def(name:'c',value:0);\n" +
                "        if(condition:'a==0',do[\n" +
                "            set(name:'b',value:1);\n" +
                "            test(desc:'b1');\n" +
                "        ]) \n" +
                "        else(condition:'a==2',do [\n" +
                "                set(name:'b',value:2);\n" +
                "                test(desc:'b2');\n" +
                "        ]) \n" +
                "        else( do [\n" +
                "            set(name:'b',value:3);\n" +
                "            set(name:'c',value:3);\n" +
                "            def(name:'d',value:3);\n" +
                "            test(desc:'b3');\n" +
                "            if(condition:'b==2',do [\n" +
                "                    set(name:'e',value:1);\n" +
                "                    test(desc:'e1');\n" +
                "            ])\n" +
                "            else(do [\n" +
                "                    set(name:'e',value:2);\n" +
                "                    test(desc:'e2');\n" +
                "            ])\n" +
                "        ]);\n" +
                "        test(desc:'d4')\n" +
                "    ])";
		str = transferJson(str).toString();
		new JSonParser().parser(str, new HashMap<String,Object>());
		
	}
	
	//测试解析执行
	@Override
	public Result testParser(String json,Map<String,Object> params){
		params.put(Attrs.ROLLBACK_FLAG, true);
		return parser(json, params);
	}
	
	//正式解析执行
	@Override
	public Result parser(String json,Map<String,Object> params){
		String errorMsg = "";
		List<Object> consoles = null;
		Object data = null;
		try {
			JSONObject root = JSONObject.fromObject(json);
		    Map<String,Object> currParams = new HashMap<String,Object>();
		    parserElement(root, currParams,params);
		    data = params.get(Attrs.RESULT_FLAG);
		    consoles = (List<Object>)params.get(Attrs.CONSOLE_VAR);
		    //销毁各种过程变量
		    destroy(params);
		} catch (Exception e) {
			e.printStackTrace();
			errorMsg = e.getMessage();
		}
		
		if(consoles == null) {
			consoles = new ArrayList<Object>();
		}
		if(StringUtils.isNotEmpty(errorMsg)) {
			consoles.add("error: "+errorMsg);
		}
	    return new Result(data, consoles);
	}
	
	@Override
	public Result parser(String str, Map<String, Object> scope,
			Map<String, Object> params) {
		// TODO Auto-generated method stub
		return null;
	}
	
	//销毁各种过程变量
	public void destroy(Map<String,Object> params) {
		params.remove(Attrs.TRANSACTIONAL);
		params.remove(Attrs.RETURN_FLAG);
		params.remove(Attrs.TASK_EXECUTOR);
	}
	
	//解析json元素
	private void parserElement(JSONObject ele1,Map<String,Object> currParams,Map<String,Object> globalParams) throws Exception {
		//当解析到return标志直接退出递归解析运行
		boolean return_flag = MapUtils.getBoolean(globalParams, Attrs.RETURN_FLAG,false);
		if(return_flag) {
			return;
		}
		//解析一个节点，如{if:{condition:'b==2',do:[{set:{name:'e',value:1}},{test:{desc:'e1'}}]}}
		TempVal val = parserOneElement(currParams,globalParams, ele1);
		//得到处理节点类如IfNode
		BaseNode node = val.getNode();
		//得到当前节点的内容，如{condition:'b==2',do:[{set:{name:'e',value:1}},{test:{desc:'e1'}}]}
		JSONObject obj = val.getObj();
		boolean flag = val.isContinue();
		//得到当前节点的子节点do属性里的节点集合，如[{set:{name:'e',value:1}},{test:{desc:'e1'}}]
		JSONArray eles = obj.optJSONArray(Attrs.DO);
		//判断是否需要继续处理当前节点的do属性的内容
		if(flag && eles != null && !eles.isEmpty()) {
			//处理当前节点内容，之前做点什么
			node.pre(currParams, globalParams, obj);
			//创建本节点下的局部参数
			Map<String,Object> params = new HashMap<String,Object>();
			/**
			 * 将处理当前节点产生的局部变量全部放入局部参数，如当前节点为if节点，里面就有过程变量，如果是否应该进入if对应的else的标志等
			 * 这里重新创建一个map，将局部参数全部加入这个map的目的是模拟嵌套标签处理时，子节点的局部变量应该包含父节点里的局部变量的情况，
			 * 也实现了参数的就近原则，如使用如下例子模拟
			   {
			   		do [
				   			i = 1;
				   			do [
				   				i = 2;
				   			]
				   			i = 3;
			   		   ]
			   }
			        当处理第一个do节点时currParams里产生了局部变量i = 1，当处理第二个do节点时新产生一个map将父节点产生的局部变量放入其中
			        这时候map作为内层do节点里的局部变量容器，当内层do执行完后，直接清空内层局部变量容器map，执行到i = 3时使用的局部变量容器
			   currParams。任意多层嵌套，都是这个规则递归下去
			 */
			params.putAll(currParams);
			for(int i = 0; i< eles.size();) {
				JSONObject ele = eles.optJSONObject(i);
				//递归解析json子元素
				parserElement(ele, params, globalParams);
				//如果循环标志不会true则解析下一个标签
				//这里表示需要重复解析这个循环标签，则i不变,反之继续处理下一个元素
				String index = getEleAttr(Attrs.INDEX, ele);
				/**
				 * 如果当前局部变量中有循环标记，或者循环变量没有结束，则i不自增，重复解析执行当前标签，知道处理后，循环标记清除后，i自增，继续处理下一个标签
				 */
				if(!"true".equals(params.get(Attrs.WHILE_FLAG)) || !"while".equals(getEleName(ele)) || index == null || !index.equals(params.get(Attrs.WHILE_INDEX))) {
					i++;
				}
			}
			node.after(currParams, globalParams, obj);
			//回收当前作用域参数
			params.clear();
			params = null;
		}
	}

	//获取指定json块的指定key的值
	private String getEleAttr(String key,JSONObject ele) {
		JSONObject target = ele.optJSONObject(getEleName(ele));
		if(target != null) {
			return target.optString(key);
		}
		return null;
	}
	
	/**
	  	获取当前json块对应的标签名，如{if:{condition:'b==2',do:[{set:{name:'e',value:1}},{test:{desc:'e1'}}]}}
	  	这个json块标签名就是if，一般都是取json块第一个key
	
	 */
	private String getEleName(JSONObject ele) {
		Set<String> names = ele.keySet();
		String eleName = null;
		if(names != null && !names.isEmpty()) {
			eleName = names.toArray(new String[names.size()])[0];
		}
		return eleName;
	}
	
	//解析一个json元素
	private TempVal parserOneElement(Map<String, Object> currParams,Map<String,Object> globalParams, JSONObject ele)  throws Exception{
		//获取该json块对应的标签名，如{if:{condition:'b==2',do:[{set:{name:'e',value:1}},{test:{desc:'e1'}}]}}的标签名就是if，
		String eleName = getEleName(ele);
		boolean isContinue = false;
		BaseNode node = null;
		if(StringUtils.isNotEmpty(eleName)) {
			/**
			 * 获取标签名对应内容json块，如{if:{condition:'b==2',do:[{set:{name:'e',value:1}},{test:{desc:'e1'}}]}}
			 * 标签名对应的内容json块就是{condition:'b==2',do:[{set:{name:'e',value:1}},{test:{desc:'e1'}}]}
			 */
			ele = ele.optJSONObject(eleName);
			//根据标签名在工厂中得到对应标签名的处理节点
			node = NodeFactory.create(eleName);
			/**
			 * 使用处理节点处理后返回是否需要继续处理标签对应内容里的子标签，一般的标签直接返回true，有些情况
			 * 如{if:{condition:'b==2',do:[{set:{name:'e',value:1}},{test:{desc:'e1'}}]}}
			 * 这里当condition里表达式不为true时，do里的内容不应该被处理，这时就需要返回false
			 */
			isContinue = node.parse(currParams, globalParams, ele);
		}
		//将是否继续处理结果，当前json块，对应的处理节点包装成临时对象返回
		return new TempVal(isContinue,ele,node);
		
	} 
	
	
	
	final static class TempVal{
		
		private boolean isContinue;
		
		private JSONObject obj;
		
		private BaseNode node;

		public TempVal(boolean isContinue, JSONObject obj,BaseNode node) {
			this.isContinue = isContinue;
			this.obj = obj;
			this.node = node;
		}

		public boolean isContinue() {
			return isContinue;
		}

		public void setContinue(boolean isContinue) {
			this.isContinue = isContinue;
		}

		public JSONObject getObj() {
			return obj;
		}

		public void setObj(JSONObject obj) {
			this.obj = obj;
		}

		public BaseNode getNode() {
			return node;
		}

		public void setNode(BaseNode node) {
			this.node = node;
		}
		
	}

}
